package com.yanghui.dtracing.core.plugin.interceptor;

import com.yanghui.dtracing.core.exception.PluginException;
import com.yanghui.dtracing.core.log.ILog;
import com.yanghui.dtracing.core.log.LogManager;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

/**
 * 静态方法拦截器
 */
public class StaticMethodIntercept {

    private static final ILog log = LogManager.getLogger(InstanceMethodIntercept.class);

    private final IStaticMethodAroundInterceptor interceptor;

    public StaticMethodIntercept(String staticMethodAroundInterceptorClassName,ClassLoader classLoader){
        try{
            this.interceptor = InterceptorInstanceLoader.load(staticMethodAroundInterceptorClassName,classLoader);
        }catch (Throwable t){
            log.error("IStaticMethodAroundInterceptor load fail.", t);
            throw new PluginException("Can't create IStaticMethodAroundInterceptor.",t);
        }
    }


    @RuntimeType
    public Object intercept(@Origin Class<?> clazz, @AllArguments Object[] allArguments, @Origin Method method,
                            @SuperCall Callable<?> callable) throws Throwable {
        /***
         * 判断是否拦截
         */
        boolean isIntercept = true;
        try{
            isIntercept = this.interceptor.isIntercept(clazz,method,allArguments);
        }catch (Throwable t){
            log.error("class[{}] isIntercept method[{}] intercept failure", clazz, method.getName(),t);
        }
        if(!isIntercept){
            return callable.call();
        }
        MethodInterceptResult methodInterceptResult = new MethodInterceptResult();
        try{
            this.interceptor.beforeMethod(clazz,method,allArguments,methodInterceptResult);
        }catch (Throwable t){
            log.error("class[{}] before method[{}] intercept failure", clazz, method.getName(),t);
        }
        Object result;
        try {
            /**调用目标方法**/
            result = callable.call();
            methodInterceptResult.setResult(result);
        }catch (Throwable t){
            try {
                methodInterceptResult.setHasException(true);
                this.interceptor.handleMethodException(clazz,method,allArguments,t,methodInterceptResult);
            }catch (Throwable throwable){
                log.error("class[{}] exception method[{}] intercept failure",clazz, method.getName(),throwable);
            }
            throw t;
        }finally {
            try {
                this.interceptor.afterMethod(clazz,method,allArguments,methodInterceptResult);
                result = methodInterceptResult.getResult();
            }catch (Throwable t){
                log.error("class[{}] after method[{}] intercept failure",clazz, method.getName(),t);
            }
        }
        return result;
    }
}
